﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace Server
{
    class Connection
    {
        private TcpListener tcpListener;
        private Thread listenThread;
        public Connection()
        {
            this.tcpListener = new TcpListener(IPAddress.Parse("0.0.0.0"), 49500);
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            this.listenThread.Start();
        }

        private void ListenForClients()
        {
            this.tcpListener.Start();

            while (true)
            {
                //blocks until a client has connected to the server
                ActiveClient tempClient = new ActiveClient(this.tcpListener.AcceptTcpClient());
                //create a thread to handle communication 
                //with connected client
                Thread clientThread = new Thread(new ThreadStart(tempClient.HandleClientComm));
                clientThread.Start();
            }
        }
    }

    class ActiveClient
    {
        public static List<ActiveClient> listClients = new List<ActiveClient>();

        TcpClient client;
        string username;
        int actualRoomID =-1;
        byte[] buffer;
        NetworkStream clientStream;
        byte[] message = new byte[4096];
        int bytesRead;

        UTF8Encoding encoder = new UTF8Encoding();

        public ActiveClient(TcpClient _Client)
        {
            client = _Client;
            client.NoDelay = true;
        }

        public void HandleClientComm()
        {
            clientStream = this.client.GetStream();

            while (true)
            {
                bytesRead = 0;

                try
                {
                    //blocks until a client sends a message
                    bytesRead = clientStream.Read(message, 0, 4096);
                }
                catch
                {
                    //a socket error has occured
                    Console.WriteLine("DEBUG: Socket error. Number of active conns: " + listClients.Count);
                    if (actualRoomID != -1)
                    {
                        listClients.Remove(this);
                        for (int i = 0; i < listClients.Count; i++)
                        {
                            if (listClients[i].actualRoomID == this.actualRoomID)
                            {
                                listClients[i].buffer = encoder.GetBytes("removeuser" + (char)2 + this.username);

                                listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                                listClients[i].clientStream.Flush();
                            }

                        }

                        Room.roomlist[actualRoomID].removeActiveUser(this.username);
                        actualRoomID = -1;
                    }
                    break;
                }

                if (bytesRead == 0)
                {
                    //the client has disconnected from the server
                    Console.WriteLine("DEBUG: Client terminated. Number of active conns: " + listClients.Count);

                    if (actualRoomID != -1)
                    {
                        listClients.Remove(this);
                        for (int i = 0; i < listClients.Count; i++)
                        {
                            if (listClients[i].actualRoomID == this.actualRoomID)
                            {
                                listClients[i].buffer = encoder.GetBytes("removeuser" + (char)2 + this.username);

                                listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                                listClients[i].clientStream.Flush();
                            }

                        }

                        Room.roomlist[actualRoomID].removeActiveUser(this.username);
                        actualRoomID = -1;
                    }
                    break;
                }

                //message has successfully been received
                string sentString = encoder.GetString(message, 0, bytesRead);
                Console.WriteLine(sentString);

                string[] splittedString = sentString.Split((char)2);

                switch (splittedString[0])
                {
                    case "login":
                        this.Login(splittedString);
                        this.RoomList();
                        Thread.Sleep(1500);
                        this.AddUser(0);
                        break;

                    case "disconnect":
                         this.RemoveUser();
                         Console.WriteLine("DEBUG: Client terminated. Number of active conns: " + listClients.Count);
                         this.Disconnect();
                        
                    break;

                    case "message":
                        this.SendMessage(sentString);
                    
                    
                    break;
                   case "userpriv":
                    for (int i = 0; i < listClients.Count; i++)
                    {
                        if (listClients[i].username==splittedString[1])
                        {
                            listClients[i].buffer = encoder.GetBytes(splittedString[0] + this.username+(char)2 + splittedString[2]);
                            listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                            listClients[i].clientStream.Flush();
                        }
                        //if (listClients[i].username == splittedString[2])
                        //{
                        //    listClients[i].buffer = encoder.GetBytes(splittedString[0] + (char)2 + splittedString[2] + (char)2 + splittedString[3]);
                        //    listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                        //    listClients[i].clientStream.Flush();
                        //}
                    }
                    break;
                    case "changeroom":
                        this.RemoveUser();
                        this.AddUser(int.Parse(splittedString[1]));
                        break;
                    case "roomlistUser":
                        Room priv = new Room(splittedString[1]);
                        //buffer=encoder.GetBytes(splittedString[0]+(char)2+splittedString[1]);
                    //
                    break;
                }
            }

            this.client.Close();
            listClients.Remove(this);

            Console.WriteLine("DEBUG: Client exited. Number of active conns: " + listClients.Count);
        }
        /// <summary>
        /// Login
        /// </summary>
        /// <param name="splittedString"></param>
        public void Login(string[] splittedString)
        {
            for (int i = 0; i < listClients.Count; i++)
            {
                if (String.Compare(listClients[i].username, splittedString[1]) == 0)
                {
                    buffer = encoder.GetBytes("acceptedlogin" + (char)2 + "0");
                    clientStream.Write(buffer, 0, buffer.Length);
                    clientStream.Flush();
                    Console.WriteLine("Login failed.");
                    return;
                }            
            }
            this.username = splittedString[1];
            buffer = encoder.GetBytes("acceptedlogin" + (char)2 + "1");
            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();
            Console.WriteLine("Login was successful.");
            
            ActiveClient.listClients.Add(this);
        }

        public void RoomList()
        {
            buffer = encoder.GetBytes("roomlist" + (char)2);
           
            for (int i = 0; i < Room.sendRoomName().Length; i++)
            {
                buffer = buffer.Concat(encoder.GetBytes(Room.sendRoomName()[i] + (char)3)).ToArray();
            }
            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();
        }
        

        public void AddUser(int _Room){
            this.actualRoomID = _Room;
            Room.roomlist[this.actualRoomID].addActiveUser(this.username);
            this.buffer = encoder.GetBytes("userlist" + (char)2);
            for (int i = 0; i < Room.roomlist[actualRoomID].sendActiveuser().Length; i++)
            {
                this.buffer = this.buffer.Concat(encoder.GetBytes(Room.roomlist[actualRoomID].sendActiveuser()[i] + (char)3)).ToArray();

            }

            clientStream.Write(buffer, 0, buffer.Length);
            clientStream.Flush();

            for (int i = 0; i < listClients.Count; i++)
            {
                if (listClients[i].actualRoomID == this.actualRoomID && listClients[i] != this)
                {
                    listClients[i].buffer = encoder.GetBytes("adduser" + (char)2 + this.username);
                    listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                    listClients[i].clientStream.Flush();

                    listClients[i].buffer = encoder.GetBytes("message" + (char)2 + this.username + " entered the room");
                    listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                    listClients[i].clientStream.Flush();
                }
            }
        }

        public void RemoveUser()
        {
            

                for (int i = 0; i < listClients.Count; i++)
                {
                    if (listClients[i].actualRoomID == this.actualRoomID && listClients[i] != this)
                    {
                        listClients[i].buffer = encoder.GetBytes("removeuser" + (char)2 + this.username);
                        listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                        listClients[i].clientStream.Flush();

                        listClients[i].buffer = encoder.GetBytes("message" + (char)2 + this.username + " left the room");
                        listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                        listClients[i].clientStream.Flush();
                    }

                }

                Room.roomlist[actualRoomID].removeActiveUser(this.username);
                actualRoomID = -1;
        }

        public void Disconnect()
        {
            listClients.Remove(this);
                this.client.Close();

                Console.WriteLine("DEBUG: Client exited. Number of active conns: " + listClients.Count);

        }

        public void SendMessage(string sentString)
        {
            for (int i = 0; i < listClients.Count; i++)
            {
                if (listClients[i].actualRoomID == this.actualRoomID)
                {
                    listClients[i].buffer = encoder.GetBytes(sentString);
                    listClients[i].clientStream.Write(listClients[i].buffer, 0, listClients[i].buffer.Length);
                    listClients[i].clientStream.Flush();
                }
                
            }
        }
    }
}
